package common.config.jwt;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import common.model.dto.ActivateEmailToken;
import common.model.dto.LoginToken;
import common.model.dto.ModifyEmailToken;
import common.model.dto.ModifyPasswordToken;
import common.util.Constants;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.Nullable;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;

/**
 * @author 米泽鹏
 */
@Slf4j
public class JwtUtil {

	/**
	 * 创建登录token
	 */
	public static TokenInfo createLoginToken(LoginToken loginToken) {
		Date nowDate = new Date();
		Date expireDate = new Date(nowDate.getTime() + TokenEnum.LOGIN.getTimeUnit().toMillis(TokenEnum.LOGIN.getExpire()));
		String token = Jwts.builder()
				.claim(TokenEnum.LOGIN.getClaimName(), JSON.toJSONString(loginToken))
				.setSubject(loginToken.getUsername() + "_" + TokenEnum.LOGIN.getClaimName())
				.setIssuer(TokenEnum.LOGIN.getIssuer())
				.signWith(SignatureAlgorithm.HS512, TokenEnum.LOGIN.getSecret())
				.setIssuedAt(nowDate)
				.setExpiration(expireDate)
				.compact();
		return new TokenInfo(token, expireDate);
	}

	/**
	 * 解析登录token
	 */
	public static LoginToken parseLoginToken(String token) throws SignatureException, MalformedJwtException, ExpiredJwtException, UnsupportedJwtException, IllegalArgumentException {
		try {
			Claims claims = Jwts.parser().setSigningKey(TokenEnum.LOGIN.getSecret()).parseClaimsJws(token).getBody();
			String infoStr = claims.get(TokenEnum.LOGIN.getClaimName(), String.class);
			return JSON.parseObject(infoStr, LoginToken.class);
		} catch (SignatureException e) {
			log.error("Invalid JWT signature.");
			log.trace("Invalid JWT signature trace:", e);
			throw e;
		} catch (MalformedJwtException e) {
			log.error("Invalid JWT token.");
			log.trace("Invalid JWT token trace:", e);
			throw e;
		} catch (ExpiredJwtException e) {
			log.error("Expired JWT token.");
			log.trace("Expired JWT token trace:", e);
			throw e;
		} catch (UnsupportedJwtException e) {
			log.error("Unsupported JWT token.");
			log.trace("Unsupported JWT token trace:", e);
			throw e;
		} catch (IllegalArgumentException e) {
			log.error("JWT token compact of handler are invalid.");
			log.trace("JWT token compact of handler are invalid trace:", e);
			throw e;
		}
	}

	/**
	 * 从请求中获取token
	 */
	public static String getTokenStringFromRequest(HttpServletRequest request) {
		if (request == null) {
			return null;
		}
		String bearerToken = request.getHeader(Constants.AUTHORIZATION_HEADER);
		if (StrUtil.isNotBlank(bearerToken)) {
			return bearerToken;
		}
		return null;
	}

	/**
	 * 从请求中获取当前登录用户的信息
	 */
	@Nullable
	public static LoginToken getLoginTokenFromRequest(HttpServletRequest request) {
		if (request == null) {
			return null;
		}
		String token = getTokenStringFromRequest(request);
		// 如果获取不到token的信息，说明没有登录的用户
		if (StrUtil.isBlank(token)) {
			return null;
		}
		return parseLoginToken(token);
	}

	/**
	 * 从请求中获取当前登录用户的用户名
	 */
	@Nullable
	public static String getUsernameFromRequest(HttpServletRequest request) {
		if (request == null) {
			return null;
		}
		LoginToken loginToken = getLoginTokenFromRequest(request);
		if (loginToken == null) {
			return null;
		}
		return loginToken.getUsername();
	}

	/**
	 * 创建修改密码token
	 */
	public static TokenInfo createModifyPasswordToken(ModifyPasswordToken modifyPasswordToken) {
		Date nowDate = new Date();
		Date expireDate = new Date(nowDate.getTime() + TokenEnum.MODIFY_PASSWORD.getTimeUnit().toMillis(TokenEnum.MODIFY_PASSWORD.getExpire()));
		String token = Jwts.builder()
				.claim(TokenEnum.MODIFY_PASSWORD.getClaimName(), JSON.toJSONString(modifyPasswordToken))
				.setSubject(modifyPasswordToken.getUserId() + "_" + TokenEnum.MODIFY_PASSWORD.getClaimName())
				.setIssuer(TokenEnum.MODIFY_PASSWORD.getIssuer())
				.signWith(SignatureAlgorithm.HS512, TokenEnum.MODIFY_PASSWORD.getSecret())
				.setIssuedAt(nowDate)
				.setExpiration(expireDate)
				.compact();
		return new TokenInfo(token, expireDate);
	}

	/**
	 * 解析修改密码token
	 */
	public static ModifyPasswordToken parseModifyPasswordToken(String token) throws SignatureException, MalformedJwtException, ExpiredJwtException, UnsupportedJwtException, IllegalArgumentException {
		try {
			Claims claims = Jwts.parser().setSigningKey(TokenEnum.MODIFY_PASSWORD.getSecret()).parseClaimsJws(token).getBody();
			String infoStr = claims.get(TokenEnum.MODIFY_PASSWORD.getClaimName(), String.class);
			return JSON.parseObject(infoStr, ModifyPasswordToken.class);
		} catch (SignatureException e) {
			log.info("Invalid JWT signature.");
			log.trace("Invalid JWT signature trace:", e);
			throw e;
		} catch (MalformedJwtException e) {
			log.info("Invalid JWT token.");
			log.trace("Invalid JWT token trace:", e);
			throw e;
		} catch (ExpiredJwtException e) {
			log.info("Expired JWT token.");
			log.trace("Expired JWT token trace:", e);
			throw e;
		} catch (UnsupportedJwtException e) {
			log.info("Unsupported JWT token.");
			log.trace("Unsupported JWT token trace:", e);
			throw e;
		} catch (IllegalArgumentException e) {
			log.info("JWT token compact of handler are invalid.");
			log.trace("JWT token compact of handler are invalid trace:", e);
			throw e;
		}
	}

	/**
	 * 创建邮箱激活token
	 */
	public static TokenInfo createActivateEmailToken(ActivateEmailToken activateEmailToken) {
		Date nowDate = new Date();
		Date expireDate = new Date(nowDate.getTime() + TokenEnum.ACTIVATE_EMAIL.getTimeUnit().toMillis(TokenEnum.ACTIVATE_EMAIL.getExpire()));
		String token = Jwts.builder()
				.claim(TokenEnum.ACTIVATE_EMAIL.getClaimName(), JSON.toJSONString(activateEmailToken))
				.setSubject(activateEmailToken.getUserId() + "_" + TokenEnum.ACTIVATE_EMAIL.getClaimName())
				.setIssuer(TokenEnum.ACTIVATE_EMAIL.getIssuer())
				.signWith(SignatureAlgorithm.HS512, TokenEnum.ACTIVATE_EMAIL.getSecret())
				.setIssuedAt(nowDate)
				.setExpiration(expireDate)
				.compact();
		return new TokenInfo(token, expireDate);
	}

	/**
	 * 解析邮箱激活token
	 */
	public static ActivateEmailToken parseActivateEmailToken(String token) throws SignatureException, MalformedJwtException, ExpiredJwtException, UnsupportedJwtException, IllegalArgumentException {
		try {
			Claims claims = Jwts.parser().setSigningKey(TokenEnum.ACTIVATE_EMAIL.getSecret()).parseClaimsJws(token).getBody();
			String infoStr = claims.get(TokenEnum.ACTIVATE_EMAIL.getClaimName(), String.class);
			return JSON.parseObject(infoStr, ActivateEmailToken.class);
		} catch (SignatureException e) {
			log.info("Invalid JWT signature.");
			log.trace("Invalid JWT signature trace:", e);
			throw e;
		} catch (MalformedJwtException e) {
			log.info("Invalid JWT token.");
			log.trace("Invalid JWT token trace:", e);
			throw e;
		} catch (ExpiredJwtException e) {
			log.info("Expired JWT token.");
			log.trace("Expired JWT token trace:", e);
			throw e;
		} catch (UnsupportedJwtException e) {
			log.info("Unsupported JWT token.");
			log.trace("Unsupported JWT token trace:", e);
			throw e;
		} catch (IllegalArgumentException e) {
			log.info("JWT token compact of handler are invalid.");
			log.trace("JWT token compact of handler are invalid trace:", e);
			throw e;
		}
	}

	/**
	 * 创建邮箱换绑token
	 */
	public static TokenInfo createModifyEmailToken(ModifyEmailToken modifyEmailToken) {
		Date nowDate = new Date();
		Date expireDate = new Date(nowDate.getTime() + TokenEnum.MODIFY_EMAIL.getTimeUnit().toMillis(TokenEnum.MODIFY_EMAIL.getExpire()));
		String token = Jwts.builder()
				.claim(TokenEnum.MODIFY_EMAIL.getClaimName(), JSON.toJSONString(modifyEmailToken))
				.setSubject(modifyEmailToken.getUserId() + "_" + TokenEnum.MODIFY_EMAIL.getClaimName())
				.setIssuer(TokenEnum.MODIFY_EMAIL.getIssuer())
				.signWith(SignatureAlgorithm.HS512, TokenEnum.MODIFY_EMAIL.getSecret())
				.setIssuedAt(nowDate)
				.setExpiration(expireDate)
				.compact();
		return new TokenInfo(token, expireDate);
	}

	/**
	 * 解析邮箱换绑token
	 */
	public static ModifyEmailToken parseModifyEmailToken(String token) throws SignatureException, MalformedJwtException, ExpiredJwtException, UnsupportedJwtException, IllegalArgumentException {
		try {
			Claims claims = Jwts.parser().setSigningKey(TokenEnum.MODIFY_EMAIL.getSecret()).parseClaimsJws(token).getBody();
			String infoStr = claims.get(TokenEnum.MODIFY_EMAIL.getClaimName(), String.class);
			return JSON.parseObject(infoStr, ModifyEmailToken.class);
		} catch (SignatureException e) {
			log.info("Invalid JWT signature.");
			log.trace("Invalid JWT signature trace:", e);
			throw e;
		} catch (MalformedJwtException e) {
			log.info("Invalid JWT token.");
			log.trace("Invalid JWT token trace:", e);
			throw e;
		} catch (ExpiredJwtException e) {
			log.info("Expired JWT token.");
			log.trace("Expired JWT token trace:", e);
			throw e;
		} catch (UnsupportedJwtException e) {
			log.info("Unsupported JWT token.");
			log.trace("Unsupported JWT token trace:", e);
			throw e;
		} catch (IllegalArgumentException e) {
			log.info("JWT token compact of handler are invalid.");
			log.trace("JWT token compact of handler are invalid trace:", e);
			throw e;
		}
	}

}
